#!/usr/bin/perl
use v5.12;

# Prints the typical use case for the assember
sub print_usage 
{
    print "\n\nUsage: asm.pl <assembly_file.asm>\n\n";
}

# Break a single line of assembly code into label, opcode, operand etc.
sub tokenize_line 
{
   my $label = " ";
   my $opcode = " ";
   my $operand = " ";

   if ($_[0] =~ /^ /)
   {
      ($opcode, $operand) = split(" ", $_[0]);
      return ($label, $opcode, $operand);
   } 
   else 
   {
      ($label, $opcode, $operand) = split(" ", $_[0]);
   }
}

sub search_in_optab
{
    my %optab = ("ADD"    =>   0x18,
                 "ADDF"   =>   0x58,
                 "ADDR"   =>   0x90,
                 "AND"    =>   0x40,
                 "CLEAR"  =>   0xB4,
                 "COMP"   =>   0x28,
                 "COMPF"  =>   0x88,
                 "COMPR"  =>   0xA0,
                 "DIV"    =>   0x24,
                 "DIVF"   =>   0x64,
                 "DIVR"   =>   0x9C,
                 "FIX"    =>   0xC4,
                 "FLOAT"  =>   0xC0,
                 "HIO"    =>   0xF4,
                 "J"      =>   0x3C,
                 "JEQ"    =>   0x30,
                 "JGT"    =>   0x34,
                 "JLT"    =>   0x38,
                 "JSUB"   =>   0x48,
                 "LDA"    =>   0x00,
                 "LDB"    =>   0x68,
                 "LDCH"   =>   0x50,
                 "LDF"    =>   0x70,
                 "LDL"    =>   0x08,
                 "LDS"    =>   0x6C,
                 "LDT"    =>   0x74,
                 "LDX"    =>   0x04,
                 "LPS"    =>   0xD0,
                 "MUL"    =>   0x20,
                 "MULF"   =>   0x60,
                 "MULR"   =>   0x98,
                 "NORM"   =>   0xC8,
                 "OR"     =>   0x44,
                 "RD"     =>   0xD8,
                 "RMO"    =>   0xAC,
                 "RSUB"   =>   0x4C,
                 "SHIFTL" =>   0xA4,
                 "SHIFTR" =>   0xA8,
                 "SIO"    =>   0xF0,
                 "SSK"    =>   0xEC,
                 "STA"    =>   0x0C,
                 "STB"    =>   0x78,
                 "STCH"   =>   0x54,
                 "STF"    =>   0x80,
                 "STI"    =>   0xD4,
                 "STL"    =>   0x14,
                 "STS"    =>   0x7C,
                 "STSW"   =>   0xE8,
                 "STT"    =>   0x84,
                 "STX"    =>   0x10,
                 "SUB"    =>   0x1C,
                 "SUBF"   =>   0x5C,
                 "SUBR"   =>   0x94,
                 "SVC"    =>   0xB0,
                 "TD"     =>   0xE0,
                 "TIO"    =>   0xF8,
                 "TIX"    =>   0x2C,
                 "TIXR"   =>   0xB8,
                 "WD"     =>   0xDC);
    exists $optab{ $_[0] };
}

my %symtab = ();

sub write_symtab_to_file
{
   open(MY_SYMTAB, ">", "symtab.out") or die "Cannot open symtab.out";
   foreach my $key (keys %symtab) 
   {
       printf MY_SYMTAB "$key\t%x\n", $symtab{$key};
   }
}

sub update_symtab
{
   my $label = $_[0];
   my $loc = $_[1];

   my $var = $_[0];
   my $addr = $_[1];

   if (exists $symtab{ $var }) 
   {
       die "Duplicate label $var\n";
   }
   $symtab{$var} = $addr;
# Checking for duplicate labels
#   open(MY_SYMTAB, "<", "symtab.out") or die "Cannot search symtab.out";
#   while (my $line = <MY_SYMTAB>)
#   {
#      if ($line =~ /$label/)
#      {
#         die "Duplicate Label: $label";
#      }
#   }
#   close MY_SYMTAB ;

# Append the new entry
#   open(MY_SYMTAB, ">>", "symtab.out") or die "Cannot search symtab.out";
#   printf(MY_SYMTAB "$label => %x\n", $loc);
#   close MY_SYMTAB ;
}

sub length_of_operand
{
   my @tokens = split("'", $_[0]);

   given ($tokens[0])
   {
      when ("C")
      {
         return length($tokens[1]);
      }
      when ("X")
      {
         my $len = length($tokens[1]) / 2;
         return $len;
      }

      default 
      {
         die "Invalid BYTE operand\n";
      }
   }
}

1;
